package com.foreveross.bsl.push.application.impl.channel.apns;

import java.io.ByteArrayInputStream;
import java.net.Proxy;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;

import javax.inject.Inject;

import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import com.foreveross.bsl.push.application.impl.DsRouterHelper;
import com.foreveross.bsl.push.application.impl.channel.BaseChannel;
import com.foreveross.bsl.push.application.impl.channel.ChannelConfigException;
import com.foreveross.bsl.push.application.impl.channel.SendTarget;
import com.foreveross.bsl.push.domain.Message;
import com.foreveross.bsl.push.domain.entity.channel.ApnsConfig;
import com.foreveross.bsl.push.domain.entity.channel.AppChannelConfig;
import com.google.common.collect.Maps;
import com.notnoop.apns.APNS;
import com.notnoop.apns.ApnsNotification;
import com.notnoop.apns.ApnsService;
import com.notnoop.apns.ApnsServiceBuilder;
import com.notnoop.apns.EnhancedApnsNotification;

@Component
public class ApnsChannel extends BaseChannel {

	private final static Logger log = LoggerFactory.getLogger(ApnsChannel.class);
	
	private final Map<String, ApnsService> servicePool = Maps.newConcurrentMap();
	private final static int DEFAULT_POOL_SIZE = 15;
	private volatile byte[] locker = new byte[0];
	private final AtomicInteger c = new AtomicInteger();

	@Inject
	protected ApnsFeedbackDelegate delegate;

	@Override
	public String getChannelId() {
		return "apns";
	}

	/**
	 * 在channel.config配置文件中使用键值:{channelId}.poolsize=10
	 * 默认poolSize为15
	 * @return
	 */
	protected int getPoolSize() {
		String strPool=this.getChannelConfig().getProperty(this.getChannelId()+".poolsize");
		int poolSize=DEFAULT_POOL_SIZE;
		try {
			poolSize=Integer.parseInt(strPool);
			if(poolSize < 1){
				poolSize=1;
			}
		} catch (NumberFormatException e) {
		}
		return poolSize;
	}

	protected ApnsConfig getApnsConfig(String appId){
		log.debug("获取APNS生产环境渠道的推送配置...");
		AppChannelConfig cfg = this.getAppChannelConfig(appId);
		return cfg.getApnsConfig();
	}

	/** 得到此应用的apns证书等配置
	 * [getAndCheckApnsConfig description]
	 * @param  appId [description]
	 * @return       [description]
	 */
	private ApnsConfig getAndCheckApnsConfig(String appId) {
		ApnsConfig apnsCfg = getApnsConfig(appId);
		StringBuilder msg = new StringBuilder("应用:" + appId + "的"+this.getChannelId()+"渠道的APNS推送配置");
		if (apnsCfg == null) {
			msg.append("未设置");
			throw new ChannelConfigException(msg.toString());
		}
		if (apnsCfg.getCertContent()==null || apnsCfg.getCertContent().length==0){
			msg.append("证书未设置");
			throw new ChannelConfigException(msg.toString());
		}
		if(StringUtils.isEmpty(apnsCfg.getCertPassword())){
			msg.append("证书密码未设置");	//是否必须设置密码？
			throw new ChannelConfigException(msg.toString());
		}
		return apnsCfg;
	}

	/** 够建一个apns服务provider
	 * [buildApnsService description]
	 * @param  apnsCfg [description]
	 * @return         [description]
	 */
	protected ApnsService buildApnsService(ApnsConfig apnsCfg) {
		// TODO BUG!! 使用APNS client多线程模式，当证书被吊销时：
		// 即出现异常Received fatal alert: certificate_revoked，且多个设备情况下,会导致应用僵死,待修复
		// 临时解决办法，更改过期证书并重启服务器
		ByteArrayInputStream certIs = new ByteArrayInputStream(apnsCfg.getCertContent());
		ApnsServiceBuilder asb = APNS.newService().withCert(certIs, apnsCfg.getCertPassword()).withProductionDestination()
				.withDelegate(this.delegate).asPool(this.getPoolSize()).asQueued();
		Proxy proxy = this.getProxy();
		if(proxy != null) {
			log.info("渠道{}使用代理服务器:{}", this.getChannelId(), proxy.toString());
			asb.withProxy(proxy);
		}
		return asb.build();
	}

	/** 从池里得到一个apns服务provider，如果没有构建一个放进池里
	 * [getApnsService description]
	 * @param  appId [description]
	 * @return       [description]
	 */
	private ApnsService getApnsService(String appId) {
		ApnsService apns = servicePool.get(appId);
		if (apns == null) {
			synchronized (locker) {
				apns = servicePool.get(appId); // 直接从队列拿出也可能apns服务过期
				if (apns == null) {
					apns = this.buildApnsService(this.getAndCheckApnsConfig(appId));
					this.servicePool.put(appId, apns);
					log.debug("为应用{}创建apnsService成功,channelId:{}",appId, this.getChannelId());
				}
			}
		}
		return apns;
	}
	
	/** 适配apns要求的格式
	 * @param target
	 * @param payload
	 * @return
	 */
	private List<ApnsNotification> bindMessageToPushs(SendTarget target, String payload) {
		String dsRouter = DsRouterHelper.getCurrentDsRouter();
		int n = 0;
		List<ApnsNotification> apnsNts = new LinkedList<ApnsNotification>();
		int apnsMsgId = 0;
		for(SendTarget.TargetIdentity ti : target.getTargets()) {
			apnsMsgId = c.incrementAndGet();
			apnsNts.add(new EnhancedApnsNotification(apnsMsgId, EnhancedApnsNotification.MAXIMUM_EXPIRY, ti.getToken(), payload));
			this.delegate.getPushBinding().put(apnsMsgId + "", ti.getPushId(), dsRouter);
			n++;
		}
		log.debug("执行此次推送前压入 {} 个推送PushBinding, 目前共 {} 个", n, this.delegate.getPushBinding().getSize());
		return apnsNts;
	}

	
	@Override
	protected void doSend(final SendTarget target, final Message msg) {
		ApnsService apns = this.getApnsService(target.getAppId());
		String payload = ApnMessageConverter.convert2ApnStyleMessage(msg);
		List<ApnsNotification> apnsNts = this.bindMessageToPushs(target, payload);
		for(ApnsNotification an : apnsNts){
			apns.push(an);
		}
		if(log.isDebugEnabled()){
			log.debug("推送到终端的消息内容({}bytes): {}", payload.getBytes().length, payload);
		}
	}

	@Override
	protected void doClose() throws Exception {
		log.debug("{}渠道的Service个数:{}",this.getChannelId(), this.servicePool.size());
		for(String key : this.servicePool.keySet()){
			ApnsService service = this.servicePool.get(key);
			service.stop();
		}
		this.servicePool.clear();
	}
}
